/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.ui.util;

import java.lang.reflect.Field;

/**
 * @author Sean W. Quinn (swquinn@gmail.com)
 *
 */
public final class ReflectionUtils {

  private ReflectionUtils() {
    // static only
  }

  /**
   * Use reflection to set an otherwise inaccessible field's value for an
   * object.
   *
   * @param o the object
   * @param fieldName the name of the field
   * @param value the value to set
   */
  public static void setFieldValue(final Object o, final String fieldName, final Object value) {
    try {
      final Field field = findField(o, fieldName);
      field.setAccessible(true);
      field.set(o, value);
    }
    catch (final IllegalAccessException e) {
      throw new IllegalStateException("Unexpected reflection exception - " + e.getClass().getName() + ": "
          + e.getMessage());
    }
  }

  /**
   * Find a field in the class hierarchy.
   *
   * @param o an instance, for instance fields, or a class for static fields.
   * @param fieldName the name of the field
   * @return the field, or <code>null</code> if not found
   */
  static Field findField(final Object o, final String fieldName) {

    Class<?> c = o instanceof Class ? (Class<?>) o : o.getClass();
    while (c != Object.class) {
      try {
        return c.getDeclaredField(fieldName);
      }
      catch (final NoSuchFieldException e) {
        c = c.getSuperclass();
      }
    }

    // will cause a NPE
    return null;
  }

  /**
   * Get a field's value.
   *
   * @param <T> the value type
   * @param o the object or the class if a static field
   * @param fieldName the field name
   * @return the field value
   */
  @SuppressWarnings("unchecked")
  public static <T> T getFieldValue(final Object o, final String fieldName) {
    try {
      final Field field = findField(o, fieldName);
      field.setAccessible(true);
      return (T) (o instanceof Class ? field.get(null) : field.get(o));
    }
    catch (final IllegalArgumentException e) {
      throw new IllegalStateException("Unexpected reflection exception - " + e.getClass().getName() + ": "
          + e.getMessage());
    }
    catch (final IllegalAccessException e) {
      throw new IllegalStateException("Unexpected reflection exception - " + e.getClass().getName() + ": "
          + e.getMessage());
    }
  }

  /**
   * Return the class for a given class name.
   *
   * @param <T> the class type
   * @param className name of class
   * @return the class
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T> forName(final String className) {
    try {
      return (Class<T>) Class.forName(className);
    }
    catch (final ClassNotFoundException e) {
      throw new IllegalStateException("Unexpected reflection exception - " + e.getClass().getName() + ": "
          + e.getMessage());
    }
  }
}
